﻿using System;
using System.Collections.Generic;
using System.Diagnostics;

using zijian666.Abstractions;
using zijian666.Core;
using zijian666.ConvertExtensions;
using zijian666.Filters;

namespace zijian666.Convertor.Base;

[DebuggerDisplay("{" + nameof(GetDebuggerDisplay) + "(),nq}")]
internal sealed class AspectConvertor<T>(IConvertor<T> convertor) : IObjectProxy, IConvertor<T>
{
    /// <summary>
    /// 显示在调试器中的内容
    /// </summary>
    private string GetDebuggerDisplay() => $"Aspect({InnerConvertor.GetType().GetFriendlyName()},out={typeof(T).GetFriendlyName()})";

    public IConvertor<T> InnerConvertor { get; } = convertor ?? throw new ArgumentNullException(nameof(convertor));

    object? IObjectProxy.GetActualObject() => InnerConvertor;

    public uint Priority => InnerConvertor.Priority;

    private readonly IEnumerable<IConvertFilter<T>> _filters = [
#if DEBUG
        new TraceFilter<T>(),
#endif
        new CustomOperatorFilter<T>()
    ];

    private static ConvertResult<T> OfType(ref object? input)
    {
        if (input is T t)
        {
            return new(t);
        }

        // 如果是代理，则获取实际对象
        if (input is IObjectProxy proxy)
        {
            input = proxy.GetActualObject();
            if (input is T t1)
            {
                return new(t1);
            }
        }
        return ConvertResult<T>.FAIL;
    }

    public ConvertResult<T> Convert(IConvertContext context, object? input)
    {
        try
        {
            ExceptionBuilder? exbuilder = null;
            var result = AspectConvertor<T>.OfType(ref input);
            if (result.Success)
            {
                return result;
            }
            result = OnConverting(context, InnerConvertor, input);
            if (result.Success)
            {
                OnConverted(context, InnerConvertor, input, result);
                return result;
            }
            ExceptionBuilder.Catch(ref exbuilder, result.Exception, "OnConverting");
            OnException(context, InnerConvertor, input, result.Exception);

            result = InnerConvertor.Convert(context, input);
            if (result.Success)
            {
                OnConverted(context, InnerConvertor, input, result);
                return result;
            }
            ExceptionBuilder.Catch(ref exbuilder, result.Exception, "Convert");
            OnException(context, InnerConvertor, input, result.Exception);

            if (context.Settings.Translators is not null)
            {
                foreach (var translator in context.Settings.Translators)
                {
                    if (translator.TryTranslate(context, input, out var translated))
                    {
                        result = AspectConvertor<T>.OfType(ref translated);
                        if (result.Success)
                        {
                            return result;
                        }
                        result = InnerConvertor.Convert(context, translated);
                        if (result.Success)
                        {
                            OnConverted(context, InnerConvertor, input, result);
                            return result;
                        }
                        ExceptionBuilder.Catch(ref exbuilder, result.Exception, "Translate:" + translator.GetType().GetFriendlyName());
                        OnException(context, InnerConvertor, translated, result.Exception);
                    }
                }
            }
            var message = ExceptionHelper.ConvertFailMessage(input, typeof(T).GetFriendlyName(), context.Settings.CultureInfo);
            if (exbuilder is null)
            {
                return new InvalidCastException(message);
            }
            return exbuilder.ToException(message);
        }
        catch (Exception e)
        {
            OnException(context, InnerConvertor, input, e);
            return e;
        }
    }

    ConvertResult<T> OnConverting(IConvertContext context, IConvertor<T> convertor, object? value)
    {
        if (_filters is null)
        {
            return default;
        }
        foreach (var filter in _filters)
        {
            var res = filter.OnConverting(context, convertor, value);
            if (res.Success)
            {
                return res;
            }
        }
        return default;
    }

    void OnConverted(IConvertContext context, IConvertor<T> convertor, object? value, ConvertResult<T> result)
    {
        if (_filters is null)
        {
            return;
        }
        foreach (var filter in _filters)
        {
            filter.OnConverted(context, convertor, value, result);
        }
    }

    void OnException(IConvertContext context, IConvertor<T> convertor, object? value, Exception? exception)
    {
        if (_filters is null || exception is null)
        {
            return;
        }
        foreach (var filter in _filters)
        {
            filter.OnException(context, convertor, value, exception);
        }
    }
}
